package ged.diagram.part;

import ged.diagram.edit.parts.DiagramEditPart;

import java.io.IOException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.ui.viewer.IViewerProvider;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.core.services.ViewService;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditorWithFlyOutPalette;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.emf.core.GMFEditingDomainFactory;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;

	/**
	 * An example showing how to create a multi-page editor.
	 * <ul>
	 * <li>page 0 contains a nested text editor.
	 * </ul>
	 */
//MultiPageEditorPart class already provides means to handle multiple editor pages
public class  MultiPageEditorPlugInDiagramEditor extends MultiPageEditorPart implements
	        IGotoMarker,IEditingDomainProvider, ISelectionProvider, IMenuListener, IViewerProvider, IAdaptable
{

	    /** the multi page editor's dirty state */
	    private boolean isDirty = false;
		
	    /** The text editor used in page 0. */
	    private GedDiagramEditor editor;

	    /** The index of the page containing the text editor */
	    private int editorIndex = 0;

	    /**
	     * Creates a multi-page editor.
	     */
	    public MultiPageEditorPlugInDiagramEditor() {
	        super();
	    }

	    /**
	     * Creates page 0 of the multi-page editor,
	     * which contains an editor.
	     */
	    void createPage0() {
	        try {
	            editor = new GedDiagramEditor();
	            editorIndex = addPage(editor, getEditorInput());
	            String name = getEditorInput().getName();
	            setPageText(editorIndex, getEditorInput().getName()); //$NON-NLS-1$
	        } catch (PartInitException e) {
	            ErrorDialog
	                    .openError(
	                            getSite().getShell(),getEditorInput().getName(), null, e.getStatus()); //$NON-NLS-1$
	        }
	        
	        ResourceSet resourceSet = editor.getEditingDomain().getResourceSet(); //ResourceSet - old
	        Resource resources2 = resourceSet.getResources().get(1); //My.ged - model
	        EObject modelRoot = (EObject) resources2.getContents().get(0);//this is the multiPages element
	        EList<EObject> numberOfDiagramsInTheMultiDiagramsElement = modelRoot.eContents();
	        for(int i = 1; i < numberOfDiagramsInTheMultiDiagramsElement.size(); i++)
	        {
	        	//if(numberOfDiagramsInTheMultiDiagramsElement.equals())
	        	List<IFile> affectedFiles = new LinkedList<IFile>();
	        	URI uri = resources2.getURI(); //URI ged
	        	final EObject selectedModelElement = numberOfDiagramsInTheMultiDiagramsElement.get(i);//modelRoot.eContents().get(1); //The diagram under the MultiDiagrams element
	        	IPath filePath;
	    		String fileName = URI.decode(uri.trimFileExtension().lastSegment());
	    		if (uri.isPlatformResource()) {
	    			filePath = new Path(uri.trimSegments(1)
	    					.toPlatformString(true));
	    		} else if (uri.isFile()) {
	    			filePath = new Path(uri.trimSegments(1).toFileString());
	    		} else {
	    			// TODO : use some default path
	    			throw new IllegalArgumentException(
	    					"Unsupported URI: " + uri); //$NON-NLS-1$
	    		}
	    		String uniqueFileName = GedDiagramEditorUtil.getUniqueFileName(filePath, fileName, "ged_diagram");
	    		
	    		//create Diagram 
	    		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		        IFile diagramFile = root.getFile(filePath.append(uniqueFileName));
		        ResourceSet resourceSet3 = new ResourceSetImpl(); //1st
		        affectedFiles.add(diagramFile);
		        
		        URI diagramModelURI = URI.createPlatformResourceURI(diagramFile
						.getFullPath().toString(), false);
		        
		        //Resource diagramResource = new Resource();
				//final Resource diagramResource = resourceSet.createResource(diagramModelURI); //2nd - does not work
		        
		        URI sourceModelURI = uri; //1st
		        final Resource modelResource = resourceSet3.getResource(sourceModelURI, true); //1st
                final Resource diagramResource = resourceSet3.createResource(diagramModelURI); //1st
				
		        TransactionalEditingDomain editingDomain = GMFEditingDomainFactory.INSTANCE.createEditingDomain();
		        //ResourceSet res = editingDomain.getResourceSet();
		        //final Resource diagramResource = editingDomain.getResourceSet().createResource(diagramModelURI); //3rd ???
		        //TransactionalEditingDomain editingDomain = editor.getEditingDomain();
	    		/*final Resource diagramResource = editingDomain.getResourceSet()
	    				.createResource(diagramModelURI);*/
	    		
	    		AbstractTransactionalCommand command = new AbstractTransactionalCommand(
	    				editingDomain,
	    				Messages.GedNewDiagramFileWizard_InitDiagramCommand,
	    				affectedFiles) {

	    			protected CommandResult doExecuteWithResult(
	    					IProgressMonitor monitor, IAdaptable info)
	    					throws ExecutionException {
	    				int diagramVID = GedVisualIDRegistry
	    						.getDiagramVisualID(selectedModelElement);
	    				if (diagramVID != DiagramEditPart.VISUAL_ID) {
	    					return CommandResult
	    							.newErrorCommandResult(Messages.GedNewDiagramFileWizard_IncorrectRootError);
	    				}
	    				Diagram diagram = ViewService.createDiagram(
	    						selectedModelElement,
	    						DiagramEditPart.MODEL_ID,
	    						GedDiagramEditorPlugin.DIAGRAM_PREFERENCES_HINT);
	    				diagramResource.getContents().add(diagram);
	    				return CommandResult.newOKCommandResult();
	    			}
	    		};
	    		try {
	    			OperationHistoryFactory.getOperationHistory().execute(command,
	    					new NullProgressMonitor(), null);
	    			diagramResource.save(GedDiagramEditorUtil.getSaveOptions());
	    		} catch (ExecutionException e) {
	    			GedDiagramEditorPlugin.getInstance().logError(
	    					"Unable to create model and diagram", e); //$NON-NLS-1$
	    		} catch (IOException ex) {
	    			GedDiagramEditorPlugin.getInstance().logError(
	    					"Save operation failed for: " + diagramModelURI, ex); //$NON-NLS-1$
	    		} 
	    			createPage2((IEditorInput)new FileEditorInput(diagramFile));
	    		//}
	        }
	        
	    }
	    
	    /**
	     * Creates page 2 of the multi-page editor,
	     * which shows the sorted text.
	     */
	    void createPage2(IEditorInput input) {
	    	{
		        try {
		        	editor = new GedDiagramEditor();
		            editorIndex = addPage(editor, input);
		            setPageText(editorIndex, getEditorInput().getName());
		        } catch (PartInitException e) {
		            ErrorDialog
	                .openError(
	                        getSite().getShell(),getEditorInput().getName(), null, e.getStatus()); //$NON-NLS-1$
		        }
		        }
	    }

	    /**
	     * Creates the pages of the multi-page editor.
	     */
	    protected void createPages() {
	    	createPage0();
	    }

	    /**
	     * Saves the multi-page editor's document.
	     */
	    public void doSave(IProgressMonitor monitor) {
	        getEditor(0).doSave(monitor);
	    }

	    /**
	     * Saves the multi-page editor's document as another file.
	     * Also updates the text for page 0's tab, and updates this multi-page editor's input
	     * to correspond to the nested editor's.
	     */
	    public void doSaveAs() {
	        IEditorPart editor = getEditor(0);
	        editor.doSaveAs();
	        setPageText(0, editor.getTitle());
	        setInput(editor.getEditorInput());
	    }

	    /**
	     * The <code>MultiPageEditorExample</code> implementation of this method
	     * checks that the input is an instance of <code>IFileEditorInput</code>.
	     */
	    public void init(IEditorSite site, IEditorInput editorInput)
	            throws PartInitException {
	        if (!(editorInput instanceof IFileEditorInput))
	            throw new PartInitException("Init"); //$NON-NLS-1$
	        super.init(site, editorInput);
	    }

	    /* (non-Javadoc)
	     * Method declared on IEditorPart.
	     */
	    public boolean isSaveAsAllowed() {
	        return true;
	    }

	    /**
	     * Calculates the contents of page 2 when the it is activated.
	     */
/*	    protected void pageChange(int newPageIndex) {
	        super.pageChange(newPageIndex);
	        if (newPageIndex == 2) {
	            //sortWords();
	        }
	    }*/

	    /* (non-Javadoc)
	     * @see org.eclipse.ui.ide.IGotoMarker
	     */
	    public void gotoMarker(IMarker marker) {
	        setActivePage(editorIndex);
	        IDE.gotoMarker((IEditorPart) editor, marker);
	    }

		@Override
		public Viewer getViewer() {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public void menuAboutToShow(IMenuManager manager) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void addSelectionChangedListener(
				ISelectionChangedListener listener) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public ISelection getSelection() {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public void removeSelectionChangedListener(
				ISelectionChangedListener listener) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void setSelection(ISelection selection) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public EditingDomain getEditingDomain() {
			// TODO Auto-generated method stub
			return null;
		}
		
		private class MultiPageCommandStackListener implements CommandStackListener
	    {

	        /** the observed command stacks */
	        private List commandStacks = new ArrayList(2);

	        /**
	         * Adds a <code>CommandStack</code> to observe.
	         * @param commandStack
	         */
	        public void addCommandStack(CommandStack commandStack)
	        {
	            commandStacks.add(commandStack);
	            commandStack.addCommandStackListener(this);
	        }

	        /* (non-Javadoc)
	         * @see org.eclipse.gef.commands.CommandStackListener#commandStackChanged(java.util.EventObject)
	         */
	        public void commandStackChanged(EventObject event)
	        {
	            if (((CommandStack) event.getSource()).isDirty())
	            {
	                // at least one command stack is dirty, 
	                // so the multi page editor is dirty too
	                setDirty(true);
	            }
	            else
	            {
	                // probably a save, we have to check all command stacks
	                boolean oneIsDirty = false;
	                for (Iterator stacks = commandStacks.iterator();
	                    stacks.hasNext();
	                    )
	                {
	                    CommandStack stack = (CommandStack) stacks.next();
	                    if (stack.isDirty())
	                    {
	                        oneIsDirty = true;
	                        break;
	                    }
	                }
	                setDirty(oneIsDirty);
	            }
	        }

	        /**
	         * Disposed the listener
	         */
	        public void dispose()
	        {
	            for (Iterator stacks = commandStacks.iterator(); stacks.hasNext();)
	            {
	                ((CommandStack) stacks.next()).removeCommandStackListener(this);
	            }
	            commandStacks.clear();
	        }

	        /**
	         * Marks every observed command stack beeing saved.
	         * This method should be called whenever the editor/model
	         * was saved.
	         */
	        public void markSaveLocations()
	        {
	            for (Iterator stacks = commandStacks.iterator(); stacks.hasNext();)
	            {
	                CommandStack stack = (CommandStack) stacks.next();
	                stack.markSaveLocation();
	            }
	        }
	    }
	    /**
	     * This class listens to changes to the file system in the workspace, and
	     * makes changes accordingly.
	     * 1) An open, saved file gets deleted -> close the editor
	     * 2) An open file gets renamed or moved -> change the editor's input accordingly
	     * 
	     */
	    private class ResourceTracker
	        implements IResourceChangeListener, IResourceDeltaVisitor
	    {
	        public void resourceChanged(IResourceChangeEvent event)
	        {
	            IResourceDelta delta = event.getDelta();
	            try
	            {
	                if (delta != null)
	                    delta.accept(this);
	            }
	            catch (CoreException exception)
	            {
	                GedDiagramEditorPlugin.getInstance().getLog().log(exception.getStatus());//----
	                exception.printStackTrace();
	            }
	        }

	        public boolean visit(IResourceDelta delta)
	        {
	            if (delta == null
	                || !delta.getResource().equals(
	                    ((IFileEditorInput) getEditorInput()).getFile()))
	                return true;

	            if (delta.getKind() == IResourceDelta.REMOVED)
	            {
	                if ((IResourceDelta.MOVED_TO & delta.getFlags()) == 0)
	                {
	                    // if the file was deleted
	                    // NOTE: The case where an open, unsaved file is deleted is being handled by the 
	                    // PartListener added to the Workbench in the initialize() method.
	                    if (!isDirty())
	                        closeEditor(false);
	                }
	                else
	                {
	                    // else if it was moved or renamed
	                    final IFile newFile =
	                        ResourcesPlugin.getWorkspace().getRoot().getFile(
	                            delta.getMovedToPath());
	                    Display display = getSite().getShell().getDisplay();
	                    display.asyncExec(new Runnable()
	                    {
	                        public void run()
	                        {
	                            setInput(new FileEditorInput(newFile));
	                        }
	                    });
	                }
	            }
	            return false;
	        }
	    }
	    
	    /**
	     * Closes this editor.
	     * @param save
	     */
	    void closeEditor(final boolean save)
	    {
	        getSite().getShell().getDisplay().syncExec(new Runnable()
	        {
	            public void run()
	            {
	                getSite().getPage().closeEditor(MultiPageEditorPlugInDiagramEditor.this, save);
	            }
	        });
	    }
	    
	    /**
	     * Changes the dirty state.
	     * @param dirty
	     */
	    private void setDirty(boolean dirty)
	    {
	        if (isDirty != dirty)
	        {
	            isDirty = dirty;
	            firePropertyChange(IEditorPart.PROP_DIRTY);
	        }
	    }
	    
	    public boolean isDirty()
	    {
	        return isDirty;
	    }
	    
	}
